import os
import sys
import cv2
import math
import time
import ctypes
import numpy as np
import tkinter as tk
import mediapipe as mp
from tkinter import ttk
from PIL import ImageTk
from typing import List, Tuple, Optional
from PIL import Image, ImageDraw, ImageFont

# 初始化MediaPipe手部检测
mp_hands = mp.solutions.hands
mp_drawing = mp.solutions.drawing_utils

# 游戏常量 - 提高刷新率相关设置
SCREEN_WIDTH, SCREEN_HEIGHT = 1280, 720  # 屏幕尺寸
TARGET_FPS = 60  # 目标帧率提高到60
FRAME_DELAY = int(1000 / TARGET_FPS)  # 帧间隔毫秒数

# 其他游戏常量
PILLAR_WIDTH = 20  # 柱子宽度
PILLAR_HEIGHT = 300  # 柱子高度
PILLAR_Y = SCREEN_HEIGHT - 100  # 柱子底部Y坐标
MIN_DISK_WIDTH = 50  # 最小盘子宽度
DISK_HEIGHT = 30  # 盘子高度
DISK_GAP = 5  # 盘子之间的间隙
HIGHLIGHT_BORDER_WIDTH = 4  # 高亮边框宽度

# 颜色定义
WHITE = (255, 255, 255)
BLACK = (30, 30, 30)
RED = (180, 50, 50)
GREEN = (50, 180, 50)
BLUE = (50, 50, 180)
YELLOW = (200, 200, 80)
GRAY = (150, 150, 150)
HIGHLIGHT_COLOR = (100, 200, 200)
HAND_MARKER_COLOR = (255, 100, 0)  # 橙色手标记
PROMPT_COLOR = (255, 100, 0)  # 橙色提示语
COMPLETE_COLOR = (0, 180, 0)  # 通关文字颜色（绿色）

# 状态定义
STATE_START_MENU = 0        # 开始菜单
STATE_INSTRUCTIONS = 1      # 玩法说明
STATE_INIT = 2              # 初始化
STATE_RESET = 3             # 重置
STATE_SELECT_DISK = 4       # 选择盘子
STATE_DISK_HIGHLIGHTED = 5  # 盘子已高亮
STATE_DRAGGING = 6          # 拖动中
STATE_HOVER_PILLAR = 7      # 悬停在柱子上
STATE_COMPLETE = 8          # 完成
STATE_EXIT = 9              # 退出

class Disk:
    def __init__(self, size: int, color: Tuple[int, int, int]):
        self.size = size
        self.color = color
        self.x = 0
        self.y = 0
        self.width = MIN_DISK_WIDTH + size * 30
        self.height = DISK_HEIGHT
        self.is_highlighted = False
        self.is_dragging = False
        self.target_x = 0
        self.target_y = 0
        self.animating = False
        self.highlight_timer = 0

    def draw(self, frame: np.ndarray):
        if self.animating:
            self.x += (self.target_x - self.x) * 0.2
            self.y += (self.target_y - self.y) * 0.2
            if abs(self.x - self.target_x) < 2 and abs(self.y - self.target_y) < 2:
                self.x = self.target_x
                self.y = self.target_y
                self.animating = False

        if self.is_highlighted:
            self.highlight_timer = (self.highlight_timer + 1) % 20
            alpha = 0.7 + 0.3 * (1 + math.cos(self.highlight_timer * 0.314)) / 2
            color = tuple(int(c1 * (1 - alpha) + c2 * alpha) for c1, c2 in zip(self.color, HIGHLIGHT_COLOR))
        else:
            color = self.color
            
        cv2.rectangle(frame, 
                      (int(self.x - self.width // 2), int(self.y - self.height // 2)),
                      (int(self.x + self.width // 2), int(self.y + self.height // 2)),
                      color, -1)
        cv2.rectangle(frame, 
                      (int(self.x - self.width // 2), int(self.y - self.height // 2)),
                      (int(self.x + self.width // 2), int(self.y + self.height // 2)),
                      BLACK, 2)

    def start_animation(self, target_x: int, target_y: int):
        self.target_x = target_x
        self.target_y = target_y
        self.animating = True

class Pillar:
    def __init__(self, x: int, color: Tuple[int, int, int]):
        self.x = x
        self.y = PILLAR_Y
        self.width = PILLAR_WIDTH
        self.height = PILLAR_HEIGHT
        self.color = color
        self.is_highlighted = False
        self.disks: List[Disk] = []

    def draw(self, frame: np.ndarray):
        cv2.rectangle(frame,
                      (int(self.x - self.width // 2), int(self.y - self.height)),
                      (int(self.x + self.width // 2), int(self.y)),
                      self.color, -1)
        base_width = MIN_DISK_WIDTH + (max([d.size for d in self.disks] + [0]) * 30) + 20
        cv2.rectangle(frame,
                      (int(self.x - base_width // 2), int(self.y)),
                      (int(self.x + base_width // 2), int(self.y + 10)),
                      self.color, -1)
        
        if self.is_highlighted:
            cv2.rectangle(frame,
                          (int(self.x - self.width // 2 - HIGHLIGHT_BORDER_WIDTH), 
                           int(self.y - self.height - HIGHLIGHT_BORDER_WIDTH)),
                          (int(self.x + self.width // 2 + HIGHLIGHT_BORDER_WIDTH), 
                           int(self.y + HIGHLIGHT_BORDER_WIDTH)),
                          HIGHLIGHT_COLOR, HIGHLIGHT_BORDER_WIDTH)
            cv2.rectangle(frame,
                          (int(self.x - base_width // 2 - HIGHLIGHT_BORDER_WIDTH), 
                           int(self.y - HIGHLIGHT_BORDER_WIDTH)),
                          (int(self.x + base_width // 2 + HIGHLIGHT_BORDER_WIDTH), 
                           int(self.y + 10 + HIGHLIGHT_BORDER_WIDTH)),
                          HIGHLIGHT_COLOR, HIGHLIGHT_BORDER_WIDTH)

    def add_disk(self, disk: Disk, animate: bool = False):
        self.disks.append(disk)
        if not animate:
            disk.x = self.x
            disk.y = self.y - len(self.disks) * (DISK_HEIGHT + DISK_GAP) + DISK_HEIGHT // 2
            disk.target_x = disk.x
            disk.target_y = disk.y
        else:
            disk.start_animation(self.x, self.y - len(self.disks) * (DISK_HEIGHT + DISK_GAP) + DISK_HEIGHT // 2)

    def remove_disk(self) -> Optional[Disk]:
        if self.disks:
            return self.disks.pop()
        return None

    def get_top_disk(self) -> Optional[Disk]:
        if self.disks:
            return self.disks[-1]
        return None

class HanoiGame:
    def __init__(self):
        self.state = STATE_START_MENU
        self.pillars = []
        self.dragging_disk = None
        self.original_pillar = None
        self.hand_state = "open"  # open, closed, unknown
        self.hand_position = (0, 0)
        self.last_hand_state = "open"
        self.state_counter = 0
        self.move_count = 0
        self.num_disks = 3
        self.menu_selection = 0
        self.menu_items = ["3 层 (简单)", "5 层 (中等)", "7 层 (困难)", "游戏说明", "退出游戏"]
        self.menu_confirm_timer = 0  # 菜单确认计时器
        self.menu_confirm_threshold = 20  # 需要20帧连续握拳才确认（约0.33秒@60fps）
        self.locked_selection = -1  # 锁定的选择项，避免握拳时选择变化
        self.state_change_cooldown = 0  # 状态变化冷却，避免快速切换

        # 处理打包后的资源路径（关键！）
        def resource_path(relative_path):
            """获取资源的绝对路径（兼容开发环境和打包后环境）"""
            try:
                # 打包后，资源会放在 _MEIPASS 临时目录
                base_path = sys._MEIPASS
            except Exception:
                # 开发环境，使用当前目录
                base_path = os.path.abspath(".")
            return os.path.join(base_path, relative_path)

        # 加载字体（优先使用打包的字体，避免依赖系统字体）
        try:
            # 假设你将 simhei.ttf 字体文件放在代码同一目录（需手动准备）
            self.font_path = resource_path("simhei.ttf")
            self.font = ImageFont.truetype(self.font_path, 30)
            self.small_font = ImageFont.truetype(self.font_path, 20)
            self.large_font = ImageFont.truetype(self.font_path, 40)
            self.xlarge_font = ImageFont.truetype(self.font_path, 60)
        except Exception as e:
            print(f"无法加载字体: {e}")
            self.font = None
            self.small_font = None
            self.large_font = None
            self.xlarge_font = None

    def get_text_size(self, draw: ImageDraw.ImageDraw, text: str, font) -> Tuple[int, int]:
        """兼容新旧版本PIL的文本尺寸获取方法"""
        if not font:
            return (len(text) * 20, 30)  # 估算更大的默认字体尺寸
            
        try:
            # 新版本PIL使用textlength和font.metrics获取尺寸
            text_width = draw.textlength(text, font=font)
            # 获取字体高度
            ascent, descent = font.getmetrics()
            text_height = ascent + descent
            return (int(text_width), text_height)
        except AttributeError:
            # 旧版本PIL使用textsize
            return draw.textsize(text, font=font)
        except Exception as e:
            print(f"获取文本尺寸出错: {e}")
            return (len(text) * 20, 30)  # fallback估算

    def init_game(self):
        """初始化游戏"""
        try:
            pillar_x_positions = [SCREEN_WIDTH // 4, SCREEN_WIDTH // 2, SCREEN_WIDTH * 3 // 4]
            self.pillars = [
                Pillar(pillar_x_positions[0], BLUE),
                Pillar(pillar_x_positions[1], GREEN),
                Pillar(pillar_x_positions[2], RED)
            ]

            colors = [RED, (200, 100, 50), YELLOW, GREEN, BLUE, (150, 50, 150), (50, 150, 150)]
            # 确保颜色列表足够长
            while len(colors) < self.num_disks:
                colors.append((np.random.randint(50, 180), np.random.randint(50, 180), np.random.randint(50, 180)))
                
            for i in range(self.num_disks, 0, -1):
                disk = Disk(i, colors[i % len(colors)])
                self.pillars[0].add_disk(disk)

            self.move_count = 0
            self.state = STATE_INIT
            self.dragging_disk = None
            self.original_pillar = None
        except Exception as e:
            print(f"游戏初始化错误: {e}")
            self.state = STATE_START_MENU

    def get_pillar_at_position(self, x: int) -> Optional[Pillar]:
        for pillar in self.pillars:
            if abs(x - pillar.x) < 100:
                return pillar
        return None

    def check_win(self) -> bool:
        return len(self.pillars[0].disks) == 0 and len(self.pillars[1].disks) == 0

    def process_hand_state(self, hand_landmarks, frame):
        if not hand_landmarks:
            if self.state_counter < 3:
                self.state_counter += 1
            else:
                self.hand_state = "unknown"
            return

        self.state_counter = 0
        
        index_finger_tip = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP]
        wrist = hand_landmarks.landmark[mp_hands.HandLandmark.WRIST]
        
        h, w, _ = frame.shape
        self.hand_position = (int(index_finger_tip.x * w), int(index_finger_tip.y * h))
        wrist_x, wrist_y = int(wrist.x * w), int(wrist.y * h)

        finger_tips = [
            mp_hands.HandLandmark.THUMB_TIP,
            mp_hands.HandLandmark.INDEX_FINGER_TIP,
            mp_hands.HandLandmark.MIDDLE_FINGER_TIP,
            mp_hands.HandLandmark.RING_FINGER_TIP,
            mp_hands.HandLandmark.PINKY_TIP
        ]
        
        finger_pips = [
            mp_hands.HandLandmark.THUMB_IP,
            mp_hands.HandLandmark.INDEX_FINGER_PIP,
            mp_hands.HandLandmark.MIDDLE_FINGER_PIP,
            mp_hands.HandLandmark.RING_FINGER_PIP,
            mp_hands.HandLandmark.PINKY_PIP
        ]
        
        closed_fingers = 0
        for tip, pip in zip(finger_tips, finger_pips):
            tip_landmark = hand_landmarks.landmark[tip]
            pip_landmark = hand_landmarks.landmark[pip]
            
            tip_dist = math.hypot(tip_landmark.x - wrist.x, tip_landmark.y - wrist.y)
            pip_dist = math.hypot(pip_landmark.x - wrist.x, pip_landmark.y - wrist.y)
            
            if tip_dist < pip_dist * 0.85:
                closed_fingers += 1

        current_state = "closed" if closed_fingers >= 3 else "open"
        # 增加状态稳定性检查
        if current_state == self.last_hand_state:
            self.hand_state = current_state
        self.last_hand_state = current_state

    def update_menu_selection(self):
        """更新菜单选择，仅在手张开时更新"""
        if self.hand_state == "unknown" or self.hand_state == "closed":
            return
            
        menu_start_y = SCREEN_HEIGHT // 3
        menu_item_height = 50
        item_index = int((self.hand_position[1] - menu_start_y) / menu_item_height)
        
        # 限制在有效范围内
        if 0 <= item_index < len(self.menu_items):
            if menu_start_y - 50 <= self.hand_position[1] <= menu_start_y + len(self.menu_items)*menu_item_height + 50:
                self.menu_selection = item_index
    
    def handle_menu_action(self):
        """处理菜单选择动作"""
        action_index = self.locked_selection if self.locked_selection != -1 else self.menu_selection
        
        try:
            if action_index == 0:
                self.num_disks = 3
                self.init_game()
            elif action_index == 1:
                self.num_disks = 5
                self.init_game()
            elif action_index == 2:
                self.num_disks = 7
                self.init_game()
            elif action_index == 3:
                self.state = STATE_INSTRUCTIONS
            elif action_index == 4:
                self.state = STATE_EXIT
        except Exception as e:
            print(f"菜单操作错误: {e}")
            self.state = STATE_START_MENU
        
        self.locked_selection = -1

    def update_state(self):
        """更新游戏状态"""
        if self.state_change_cooldown > 0:
            self.state_change_cooldown -= 1
            return
            
        if self.state == STATE_START_MENU:
            self.update_menu_selection()
            
            if self.hand_state == "closed":
                if self.menu_confirm_timer == 0:
                    self.locked_selection = self.menu_selection
                self.menu_confirm_timer += 1
                
                if self.menu_confirm_timer >= self.menu_confirm_threshold:
                    self.handle_menu_action()
                    self.menu_confirm_timer = 0
                    self.state_change_cooldown = 20  # 20帧冷却
            else:
                self.menu_confirm_timer = 0
                self.locked_selection = -1

        elif self.state == STATE_INSTRUCTIONS:
            if self.hand_state == "closed":
                self.menu_confirm_timer += 1
                if self.menu_confirm_timer >= self.menu_confirm_threshold:
                    self.state = STATE_START_MENU
                    self.menu_confirm_timer = 0
                    self.state_change_cooldown = 10
            else:
                self.menu_confirm_timer = 0

        elif self.state == STATE_INIT:
            self.state = STATE_RESET

        elif self.state == STATE_RESET:
            for pillar in self.pillars:
                pillar.is_highlighted = False
                for disk in pillar.disks:
                    disk.is_highlighted = False
                    disk.is_dragging = False
            self.dragging_disk = None
            self.state = STATE_SELECT_DISK

        elif self.state == STATE_SELECT_DISK:
            hand_x, _ = self.hand_position
            current_pillar = self.get_pillar_at_position(hand_x)
            
            for pillar in self.pillars:
                for disk in pillar.disks:
                    disk.is_highlighted = False
            
            if current_pillar and current_pillar.get_top_disk():
                current_pillar.get_top_disk().is_highlighted = True
                self.state = STATE_DISK_HIGHLIGHTED
            else:
                self.state = STATE_SELECT_DISK

        elif self.state == STATE_DISK_HIGHLIGHTED:
            hand_x, _ = self.hand_position
            current_pillar = self.get_pillar_at_position(hand_x)
            top_disk = current_pillar.get_top_disk() if current_pillar else None
            
            if self.hand_state == "closed" and top_disk and top_disk.is_highlighted:
                self.dragging_disk = top_disk
                self.original_pillar = current_pillar
                self.dragging_disk.is_dragging = True
                self.dragging_disk.is_highlighted = True
                self.original_pillar.remove_disk()
                self.state = STATE_DRAGGING
                self.state_change_cooldown = 5
                
            elif self.hand_state == "open":
                self.state = STATE_RESET

        elif self.state == STATE_DRAGGING:
            if self.dragging_disk:
                self.dragging_disk.x, self.dragging_disk.y = self.hand_position
                
                if self.hand_state == "open":
                    self.original_pillar.add_disk(self.dragging_disk, animate=True)
                    self.dragging_disk.is_dragging = False
                    self.dragging_disk = None
                    self.original_pillar = None
                    self.state = STATE_RESET
                    self.state_change_cooldown = 5
                else:
                    self.state = STATE_HOVER_PILLAR

        elif self.state == STATE_HOVER_PILLAR:
            hand_x, _ = self.hand_position
            current_pillar = self.get_pillar_at_position(hand_x)
            
            for pillar in self.pillars:
                pillar.is_highlighted = False
            
            if current_pillar:
                current_pillar.is_highlighted = True
                
                if self.hand_state == "open" and self.dragging_disk:
                    top_disk = current_pillar.get_top_disk()
                    if not top_disk or self.dragging_disk.size < top_disk.size:
                        current_pillar.add_disk(self.dragging_disk, animate=True)
                        self.dragging_disk.is_dragging = False
                        self.dragging_disk = None
                        self.move_count += 1
                        
                        if self.check_win():
                            self.state = STATE_COMPLETE
                        else:
                            self.state = STATE_RESET
                        self.state_change_cooldown = 5
                    else:
                        self.original_pillar.add_disk(self.dragging_disk, animate=True)
                        self.dragging_disk.is_dragging = False
                        self.dragging_disk = None
                        self.state = STATE_RESET
                        self.state_change_cooldown = 5
            else:
                if self.hand_state == "open" and self.dragging_disk:
                    self.original_pillar.add_disk(self.dragging_disk, animate=True)
                    self.dragging_disk.is_dragging = False
                    self.dragging_disk = None
                    self.state = STATE_RESET
                    self.state_change_cooldown = 5

        elif self.state == STATE_COMPLETE:
            if self.hand_state == "closed":
                self.menu_confirm_timer += 1
                if self.menu_confirm_timer >= self.menu_confirm_threshold:
                    self.init_game()
                    self.menu_confirm_timer = 0
                    self.state_change_cooldown = 10
            else:
                self.menu_confirm_timer = 0

    def draw_start_menu(self, frame: np.ndarray):
        frame.fill(245)
        pil_img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
        draw = ImageDraw.Draw(pil_img)
        
        self.put_chinese_text(draw, "手势控制汉诺塔", 
                             (SCREEN_WIDTH//2 - 150, SCREEN_HEIGHT//6), 40)
        
        menu_start_y = SCREEN_HEIGHT // 3
        menu_item_height = 50
        
        for i, item in enumerate(self.menu_items):
            y_pos = menu_start_y + i * menu_item_height
            color = PROMPT_COLOR if i == self.menu_selection else BLACK
            if i == self.menu_selection:
                draw.rectangle([(SCREEN_WIDTH//2 - 150, y_pos - 20), 
                                (SCREEN_WIDTH//2 + 150, y_pos + 20)], 
                               fill=(230, 230, 230))
            self.put_chinese_text(draw, item, (SCREEN_WIDTH//2 - 140, y_pos - 15), 30, color)
        
        if self.menu_confirm_timer > 0:
            progress = min(100, int(self.menu_confirm_timer / self.menu_confirm_threshold * 100))
            self.put_chinese_text(draw, f"确认中: {progress}%", 
                                 (SCREEN_WIDTH//2 - 80, SCREEN_HEIGHT - 150), 25, PROMPT_COLOR)
        
        self.put_chinese_text(draw, "移动手选择选项，长按握拳确认", 
                             (SCREEN_WIDTH//2 - 180, SCREEN_HEIGHT - 100), 25)
        
        if self.hand_state != "unknown":
            cv2.circle(np.array(pil_img), self.hand_position, 10, HAND_MARKER_COLOR, -1)
            cv2.circle(np.array(pil_img), self.hand_position, 15, HAND_MARKER_COLOR, 2)
        
        frame[:] = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)

    def draw_instructions(self, frame: np.ndarray):
        frame.fill(245)
        pil_img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
        draw = ImageDraw.Draw(pil_img)
        
        self.put_chinese_text(draw, "游戏玩法说明", 
                             (SCREEN_WIDTH//2 - 150, SCREEN_HEIGHT//8), 40)
        
        instructions = [
            "1. 将手移动到想要移动的盘子上方",
            "2. 握拳以抓取盘子",
            "3. 移动手将盘子带到目标柱子",
            "4. 张开手放下盘子",
            "5. 规则：大盘不能放在小盘上面",
            "6. 将所有盘子移到最右边的柱子获胜",
            "",
            "按 R 键重新开始游戏",
            "按 Q 键退出游戏",
            "按 M 键返回主菜单",
            "",
            "长按握拳返回主菜单"
        ]
        
        start_y = SCREEN_HEIGHT//4
        line_height = 35
        
        for i, line in enumerate(instructions):
            self.put_chinese_text(draw, line, (SCREEN_WIDTH//6, start_y + i * line_height), 25)
        
        if self.menu_confirm_timer > 0:
            progress = min(100, int(self.menu_confirm_timer / self.menu_confirm_threshold * 100))
            self.put_chinese_text(draw, f"返回中: {progress}%", 
                                 (SCREEN_WIDTH//2 - 80, SCREEN_HEIGHT - 100), 25, PROMPT_COLOR)
        
        if self.hand_state != "unknown":
            cv2.circle(np.array(pil_img), self.hand_position, 10, HAND_MARKER_COLOR, -1)
            cv2.circle(np.array(pil_img), self.hand_position, 15, HAND_MARKER_COLOR, 2)
        
        frame[:] = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)

    def draw(self, frame: np.ndarray):
        if self.state == STATE_START_MENU:
            self.draw_start_menu(frame)
            return
        elif self.state == STATE_INSTRUCTIONS:
            self.draw_instructions(frame)
            return
        
        frame.fill(245)
        
        for pillar in self.pillars:
            pillar.draw(frame)
        
        for pillar in self.pillars:
            for disk in pillar.disks:
                disk.draw(frame)
        
        if self.dragging_disk:
            self.dragging_disk.draw(frame)
        
        pil_img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
        draw = ImageDraw.Draw(pil_img)
        
        # 左上角手状态
        self.put_chinese_text(draw, f"手状态: {self.hand_state}", (20, 20), 25)
        
        # 右上角移动次数
        self.put_chinese_text(draw, f"移动次数: {self.move_count}", 
                             (SCREEN_WIDTH - 200, 20), 25)
        
        # 提示语
        prompt_text = ""
        prompt_font = self.font
        if self.state in [STATE_SELECT_DISK, STATE_DISK_HIGHLIGHTED]:
            prompt_text = "移动手到要移动的盘子上方，握拳抓取"
        elif self.state in [STATE_DRAGGING, STATE_HOVER_PILLAR]:
            prompt_text = "移动到目标柱子上方，张开手放下"
        elif self.state == STATE_COMPLETE:
            # 通关提示：放大并居中显示
            prompt_text = f"恭喜通关! 总移动次数: {self.move_count}"
            sub_text = "长按握拳重新开始游戏，或按M返回主菜单"
            prompt_font = self.xlarge_font  # 使用超大字体
            
            # 绘制主通关文字（居中偏上）
            text_width, text_height = self.get_text_size(draw, prompt_text, prompt_font)
            main_x = SCREEN_WIDTH // 2 - text_width // 2
            main_y = SCREEN_HEIGHT // 3  # 中间偏上位置
            # 添加半透明背景
            draw.rectangle([
                (main_x - 20, main_y - text_height),
                (main_x + text_width + 20, main_y + 10)
            ], fill=(255, 255, 255, 180))  # 半透明白色背景
            self.put_chinese_text(draw, prompt_text, (main_x, main_y - text_height), 60, COMPLETE_COLOR)
            
            # 绘制副标题
            sub_width, sub_height = self.get_text_size(draw, sub_text, self.font)
            sub_x = SCREEN_WIDTH // 2 - sub_width // 2
            sub_y = main_y + 30  # 主标题下方
            self.put_chinese_text(draw, sub_text, (sub_x, sub_y), 30, PROMPT_COLOR)
            
            # 显示确认进度
            if self.menu_confirm_timer > 0:
                progress = min(100, int(self.menu_confirm_timer / self.menu_confirm_threshold * 100))
                self.put_chinese_text(draw, f"准备重新开始: {progress}%", 
                                     (SCREEN_WIDTH//2 - 120, SCREEN_HEIGHT - 50), 25, COMPLETE_COLOR)
        
        # 非通关状态的提示语
        if prompt_text and self.state != STATE_COMPLETE:
            text_width, text_height = self.get_text_size(draw, prompt_text, prompt_font)
            draw.rectangle([(SCREEN_WIDTH//2 - text_width//2 - 10, 
                            SCREEN_HEIGHT - 80), 
                           (SCREEN_WIDTH//2 + text_width//2 + 10, 
                            SCREEN_HEIGHT - 30)], 
                          fill=WHITE)
            self.put_chinese_text(draw, prompt_text, 
                                 (SCREEN_WIDTH//2 - text_width//2, 
                                  SCREEN_HEIGHT - 70), 25, PROMPT_COLOR)
        
        frame[:] = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
        
        if self.hand_state != "unknown":
            cv2.circle(frame, self.hand_position, 10, HAND_MARKER_COLOR, -1)
            cv2.circle(frame, self.hand_position, 15, HAND_MARKER_COLOR, 2)

    def put_chinese_text(self, draw: ImageDraw.ImageDraw, text: str, position: Tuple[int, int], size: int, color=BLACK):
        if self.font:
            try:
                if size == 20:
                    font = self.small_font
                elif size == 40:
                    font = self.large_font
                elif size == 60:
                    font = self.xlarge_font
                else:
                    font = self.font
                draw.text(position, text, font=font, fill=color)
            except:
                draw.text(position, text, fill=color)
        else:
            draw.text(position, text, fill=color)

    def handle_key(self, key):
        if key == 'r' or key == 'R':
            self.init_game()
        elif key == 'q' or key == 'Q':
            self.state = STATE_EXIT
        elif key == 'm' or key == 'M':
            self.state = STATE_START_MENU
            self.state_change_cooldown = 10

def set_english_input():
    """智能切换到英文模式（仅在当前为中文模式时操作）"""
    try:
        user32 = ctypes.WinDLL('user32', use_last_error=True)
        imm32 = ctypes.WinDLL('imm32', use_last_error=True)  # 输入法相关API
        
        # 1. 获取当前窗口的输入法上下文
        hwnd = user32.GetForegroundWindow()  # 当前激活窗口
        hIMC = imm32.ImmGetContext(hwnd)    # 输入法上下文
        
        # 2. 获取输入法转换状态（判断中英文模式）
        lpConversionStatus = ctypes.c_uint()  # 存储转换状态
        lpSentenceStatus = ctypes.c_uint()    # 存储句子状态
        imm32.ImmGetConversionStatus(hIMC, ctypes.byref(lpConversionStatus), ctypes.byref(lpSentenceStatus))
        
        # 3. 判断是否为中文模式（适用于Win10微软拼音等主流输入法）
        # 标志位说明：fOpen=1表示中文模式，0表示英文模式（低16位的第0位）
        is_chinese_mode = (lpConversionStatus.value & 0x0001) == 0x0001
        
        # 4. 仅在中文模式时，模拟按Shift键切换到英文
        if is_chinese_mode:
            VK_SHIFT = 0x10
            KEYEVENTF_KEYUP = 0x0002
            user32.keybd_event(VK_SHIFT, 0, 0, 0)  # 按下Shift
            user32.keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0)  # 释放Shift
            print("已从中文模式切换到英文模式")
        else:
            print("当前已是英文模式，无需切换")
        
        # 释放输入法上下文
        imm32.ImmReleaseContext(hwnd, hIMC)
        
    except Exception as e:
        print(f"输入法状态检测/切换出错: {e}")

def get_resource_path(relative_path):
    """获取资源文件的绝对路径（适配开发和打包环境）"""
    if getattr(sys, 'frozen', False):
        # 打包后的环境：资源文件在 sys._MEIPASS 目录（单文件模式）或 EXE 同目录（文件夹模式）
        base_path = sys._MEIPASS
    else:
        # 开发环境：资源文件在当前脚本所在目录
        base_path = os.path.dirname(os.path.abspath(__file__))
    return os.path.join(base_path, relative_path)

def main():
    try:
        # 初始化Tkinter窗口
        root = tk.Tk()
        root.title("手势控制汉诺塔")
        root.withdraw()  # 先隐藏窗口以防闪烁
        # 设置窗口图标
        icon_path = get_resource_path("ico.ico")  # 替换为你的ico文件路径
        root.iconbitmap(icon_path)

        # 设置窗口大小
        window_width = SCREEN_WIDTH
        window_height = SCREEN_HEIGHT
        # 计算屏幕中心坐标
        screen_width = root.winfo_screenwidth()  # 获取屏幕宽度
        screen_height = root.winfo_screenheight()  # 获取屏幕高度
        # 窗口左上角坐标 = (屏幕宽-窗口宽)/2 , (屏幕高-窗口高)/2
        x = (screen_width - window_width) // 2
        y = (screen_height - window_height) // 2
        # 设置窗口位置
        root.geometry(f"{window_width}x{window_height}+{x}+{y}")
        root.geometry(f"{SCREEN_WIDTH}x{SCREEN_HEIGHT}")    # 设置窗口大小为1280x720
        root.minsize(SCREEN_WIDTH, SCREEN_HEIGHT)  # 设置最小窗口大小

        # 窗口获得焦点时，再次强制切换到英文输入法（关键！）
        def on_window_focus(event):
            # 延迟100ms执行，确保窗口完全激活后再切换
            root.after(100, set_english_input)
        
        # 绑定窗口焦点事件
        root.bind('<FocusIn>', on_window_focus)
        root.deiconify()  # 显示窗口（此时会触发FocusIn事件）

        # 创建显示画面的标签
        label = ttk.Label(root)  # 创建标签用于显示画面
        label.pack(fill=tk.BOTH, expand=True)   # 填充整个窗口

        # 初始化摄像头
        cap = cv2.VideoCapture(0)
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)  # 降低分辨率
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
        cap.set(cv2.CAP_PROP_FPS, 60)  # 设置帧率为60

        cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, 1)  # 自动曝光
        cap.set(cv2.CAP_PROP_AUTOFOCUS, 1)  # 自动对焦
        
        game = HanoiGame()

        set_english_input()  # 确保输入法为英文状态

        with mp_hands.Hands(
            model_complexity=0,  # 0: 轻量级模型（速度快，精度较低），1: 中等复杂度模型，2: 高精度模型（速度较慢，精度高）
            min_detection_confidence=0.7,   # 表示检测的最小置信度，0.7表示70%
            min_tracking_confidence=0.7,    # 表示跟踪的最小置信度，0.7表示70%
            max_num_hands=1) as hands:      # 表示最多检测1个手势
            
            def update_frame():
                nonlocal cap, game, root
                success, image = cap.read()  # 读取一帧图像的成功标志和图像数据
                if not success:
                    root.after(10, update_frame)
                    return
                
                try:
                    image = cv2.cvtColor(cv2.flip(image, 1), cv2.COLOR_BGR2RGB)
                    image.flags.writeable = False
                    results = hands.process(image)
                    
                    image.flags.writeable = True
                    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
                    
                    game.process_hand_state(results.multi_hand_landmarks[0] if results.multi_hand_landmarks else None, image)
                    
                    if game.state != STATE_EXIT:
                        game.update_state()
                    
                    game.draw(image)
                    
                    # 转换为Tkinter可用的图像格式
                    img = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
                    imgtk = ImageTk.PhotoImage(image=img)
                    label.imgtk = imgtk
                    label.config(image=imgtk)
                except Exception as e:
                    print(f"处理帧时出错: {e}")
                
                # 检查是否需要退出
                if game.state == STATE_EXIT:
                    cap.release()
                    root.destroy()
                    return
                root.after(FRAME_DELAY, update_frame)  # 控制帧率为60 FPS
            
            # 启动帧更新循环
            update_frame()
            root.bind('<Escape>', lambda e: [cap.release(), root.destroy()])    # 按ESC键退出
            root.bind('<Key>', lambda e: game.handle_key(e.keysym))               # 按其他键处理游戏操作
            root.mainloop()
        
    except Exception as e:
        print(f"程序错误: {e}")
        time.sleep(2)

if __name__ == "__main__":
    main()
